/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.executable;

import com.softwarecraftsmen.orogeny.application.Application;
import com.softwarecraftsmen.orogeny.buildLogs.Verbosity;
import com.softwarecraftsmen.orogeny.properties.Evaluatable;
import com.softwarecraftsmen.orogeny.properties.PropertyName;
import com.softwarecraftsmen.orogeny.taskNames.TaskName;
import com.softwarecraftsmen.orogeny.taskNames.TaskName.SomeTaskName;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;

import static com.softwarecraftsmen.orogeny.buildLogs.SimpleIndentingBuildLog.consoleIndentingBuildLog;
import static com.softwarecraftsmen.orogeny.buildLogs.Verbosity.AllDetails;
import static com.softwarecraftsmen.orogeny.taskNames.TaskName.DefaultTaskName;
import static java.lang.Runtime.getRuntime;
import static java.lang.String.format;
import static java.lang.System.err;
import static java.util.Locale.UK;

public final class ConsoleEntryPoint
{
    // From sysexits.h, a command line usage error
    private static final int GeneralError = 1;
    private static final int Help = 2;
    private static final int CommandLineUsageError = 64;
    private static final int IoError = 74;
    private static final int InternalSoftwareError = 70;

    public static void main(@NotNull final String... commandLineArguments)
    {
        final OptionParser optionParser = new OptionParser()
        {{
                accepts("help", "show help").forHelp();
                accepts("script", "Absolute or relative path to a script").withRequiredArg().ofType(File.class).required();
                accepts("task", "The task to execute (default is first non-hidden task)").withRequiredArg().ofType(SomeTaskName.class);
                accepts("verbosity", format(UK, "The verbosity %1$s", Arrays.toString(Verbosity.values()))).withRequiredArg().ofType(Verbosity.class).defaultsTo(AllDetails);
            }};

        final OptionSet optionSet;
        try
        {
            optionSet = optionParser.parse(commandLineArguments);
        }
        catch (OptionException ignored)
        {
            exitError(optionParser, "invalid command line arguments", CommandLineUsageError);
            return;
        }

        if (optionSet.has("help"))
        {
            exitError(optionParser, "Help", Help);
        }

        @Nullable final File script = (File) optionSet.valueOf("script");
        if (script == null)
        {
            exitError(optionParser, "script must be specified", CommandLineUsageError);
            return;
        }
        if (!script.exists() && !script.canRead())
        {
            exitError(optionParser, "script must exist and be readable", IoError);
            return;
        }

        @Nullable final SomeTaskName possibleTaskName = (SomeTaskName) optionSet.valueOf("task");
        @NotNull final TaskName taskName = possibleTaskName == null ? DefaultTaskName : possibleTaskName;

        final Verbosity verbosity = (Verbosity) optionSet.valueOf("verbosity");

        try
        {
            final Application application = new Application(script, taskName, consoleIndentingBuildLog(verbosity.allImpliedVerbosities()), new LinkedHashMap<PropertyName, Evaluatable>());
            if (application.run())
            {
                return;
            }
            exitError(optionParser, "BUILD FAILED", GeneralError);
        }
        catch (Exception e)
        {
            exitError(optionParser, e.getMessage(), InternalSoftwareError);
        }
    }

    private static void exitError(final OptionParser optionParser, final String message, final int exitStatusCode)
    {
        err.print("orogeny: ");
        err.println(message);
        printHelp(optionParser);
        err.flush();
        exit(exitStatusCode);
    }

    private static void printHelp(OptionParser optionParser)
    {
        try
        {
            optionParser.printHelpOn(err);
        }
        catch (IOException ignored)
        {
        }
    }

    private static void exit(final int exitStatusCode)
    {
        getRuntime().exit(exitStatusCode);
    }
    private ConsoleEntryPoint()
    {}
}
